CloudFrontでファイル名を含まないURLや末尾スラッシュ付与などの加工処理をCloudFormationから設定してみる
いわさです。
CloudFront+S3の静的サイト構成をよく組むと思います。
その時、S3の静的サイトホスティングを有効にするかしないかの選択肢が出てきます。
軽くまとめると以下のような感じでしょうか。
- 静的サイトホスティングなS3のメリット
- リダイレクトルールが設定出来る
- 非静的サイトホスティングなS3のメリット
- CloudFrontとS3の間をHTTPSに出来る
- OAIを使ってS3への直接アクセスを抑制出来る
もし、どちらのメリットも享受したい場合は、現時点では非静的サイトホスティングなS3をオリジンとしつつ、エッジでリダイレクトやオリジンURLの調整をしてやるのが、ケースとしては多いのかなと思っています。
そこで、本日は一番よく使いそうな設定をCloudFormationにしておきたかったので記事にまとめておきました。
リポジトリ
以下のリポジトリにテンプレートを置いておきます。
設定している内容
CloudFront Functionsを使って、ビューワーリクエストで以下を設定しています。
Lambda@EdgeとCloudFront Functionsのどちらで実装すべきかはこの記事では言及しませんが、AWSのサンプルにあわせてCloudFront Functionsで実装しています。
index.html を追加してファイル名を含まない URL をリクエストする - Amazon CloudFront
########################################## # CloudFront Functions ########################################## ModifyRequestUrlFunction: Type: AWS::CloudFront::Function Properties: Name: modify-request-url-cf2 AutoPublish: true FunctionConfig: Comment: https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/example-function-add-index.html Runtime: cloudfront-js-1.0 FunctionCode: | function handler(event) { var request = event.request; var uri = request.uri; // Check whether the URI is missing a file name. if (uri.endsWith('/')) { request.uri += 'index.html'; } // Check whether the URI is missing a file extension. else if (!uri.includes('.')) { request.uri += '/index.html'; } return request; } ########################################## # CloudFront ########################################## AssetsDistribution: Type: AWS::CloudFront::Distribution Properties: DistributionConfig: PriceClass: PriceClass_All Origins: - Id: !Sub ${AWS::StackName}-origin-primary DomainName: !GetAtt OriginBucket.RegionalDomainName S3OriginConfig: OriginAccessIdentity: !Sub origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity} Enabled: true DefaultRootObject: index.html DefaultCacheBehavior: ... FunctionAssociations: - EventType: viewer-request FunctionARN: !GetAtt ModifyRequestUrlFunction.FunctionMetadata.FunctionARN HttpVersion: http2
オリジンS3バケットには、hogeディレクトリとその中にindex.htmlが存在しています。
デプロイ後、HTTPクライアントでアクセス確認してみましょう。
iwasa.takahito@hoge aws-cfn-cloudfront-viewer-url % curl -I https://d1yqpd6yikj21d.cloudfront.net/hoge/ HTTP/2 200 content-type: text/html content-length: 284 last-modified: Tue, 25 Jan 2022 01:55:06 GMT x-amz-server-side-encryption: AES256 accept-ranges: bytes server: AmazonS3 date: Wed, 26 Jan 2022 00:20:18 GMT etag: "6a7f3791753d07bdd2c884213521c2a4" x-cache: RefreshHit from cloudfront via: 1.1 0d3f96f58ac3ef451aa652616a3206fc.cloudfront.net (CloudFront) x-amz-cf-pop: NRT51-C4 x-amz-cf-id: _ALe2uoVmbwcCIrmi0uLX-3fDEouv3TfTILUPh7q_enHlXsCXX0JBg== iwasa.takahito@hoge aws-cfn-cloudfront-viewer-url % curl -I https://d1yqpd6yikj21d.cloudfront.net/hoge HTTP/2 200 content-type: text/html content-length: 284 last-modified: Tue, 25 Jan 2022 01:55:06 GMT x-amz-server-side-encryption: AES256 accept-ranges: bytes server: AmazonS3 date: Wed, 26 Jan 2022 00:20:18 GMT etag: "6a7f3791753d07bdd2c884213521c2a4" x-cache: Hit from cloudfront via: 1.1 27366235f7cfef185b99df4aa8a4c352.cloudfront.net (CloudFront) x-amz-cf-pop: NRT51-C4 x-amz-cf-id: SBP_zC2eTpOdbjzSZwhoukVP8CY7TFdnqW87w4aPc9i4hSa9j1-Cgw== age: 3 iwasa.takahito@hoge aws-cfn-cloudfront-viewer-url % curl https://d1yqpd6yikj21d.cloudfront.net/hoge/ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> iwasa hoge/index.html </body> </html>%
設定前は、ステータス403になっていましたが、200でindex.htmlの内容が取得出来ています。
良いですね。
トレイリングスラッシュなしの場合にありでリダイレクトさせる
次はURLの末尾がスラッシュでない場合に、そのままあるいはURL加工のうえオリジン転送するのではなく、一度クライアントでリダイレクト処理をさせてみます。
ビューワーリクエストでリダイレクト方法は以下で紹介されているので、こちらを応用します。
ビューワーを新しい URL にリダイレクトさせる - Amazon CloudFront
ビューワーリクエストにつきひとつの関数しか設定出来ないので、先程の関数の「/で終わっていないURLで.が無い場合」の分岐でリダイレクトを設定してみます。
ModifyRequestUrlFunction: Type: AWS::CloudFront::Function Properties: Name: modify-request-url-cf2 AutoPublish: true FunctionConfig: Comment: https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/example-function-add-index.html Runtime: cloudfront-js-1.0 FunctionCode: | function handler(event) { var request = event.request; var uri = request.uri; // Check whether the URI is missing a file name. if (uri.endsWith('/')) { request.uri += 'index.html'; } // Check whether the URI is missing a file extension. else if (!uri.includes('.')) { var response = { statusCode: 302, statusDescription: 'Found', headers: { "location": { "value": request.uri + '/' }} } return response; } return request; }
確認してみます。
iwasa.takahito@hoge aws-cfn-cloudfront-viewer-url % curl -I https://d1yqpd6yikj21d.cloudfront.net/hoge/ HTTP/2 200 content-type: text/html content-length: 284 last-modified: Tue, 25 Jan 2022 01:55:06 GMT x-amz-server-side-encryption: AES256 accept-ranges: bytes server: AmazonS3 date: Wed, 26 Jan 2022 00:27:24 GMT etag: "6a7f3791753d07bdd2c884213521c2a4" x-cache: RefreshHit from cloudfront via: 1.1 58ef75a5fdb60c073729be8392b4c628.cloudfront.net (CloudFront) x-amz-cf-pop: NRT51-C4 x-amz-cf-id: Tb-cSXFRUCbLhtYRCZfHX5_LedJZlD92Eh6ejzqOK-sbGlnCi-0opQ== iwasa.takahito@hoge aws-cfn-cloudfront-viewer-url % curl -I https://d1yqpd6yikj21d.cloudfront.net/hoge HTTP/2 302 server: CloudFront date: Wed, 26 Jan 2022 00:27:28 GMT content-length: 0 location: /hoge/ x-cache: FunctionGeneratedResponse from cloudfront via: 1.1 7bed027509794290f6c6a30b859ffb1a.cloudfront.net (CloudFront) x-amz-cf-pop: NRT51-C4 x-amz-cf-id: RvjRO6VrsEWEwfGFgG1JG9DLLjQm8LTAq9IVTH7xxtLTdfKiWKSw1w==
先程までhoge/index.html
の内容が表示されていましたが、一度hoge/
へリダイレクト出来ていますね。
上記のコードでは、ステータスを302にしています。
必要に応じてリダイレクト要件にあわせて変更してください。
さいごに
本日はCloudFront+S3(非静的ウェブサイトホスティング)でよく使いそうな2種類のリダイレクト処理とオリジン転送をCloudFormationで構成してみました。
類似の記事はいくつかDevIOでも存在していますが、CloudFormation+CloudFront Functionsで構築するパターンが見当たらなかったので記事にしてみました。
Lambda@Edgeだともう少し導入の手間がありそうですが、CloudFront Functionsはお手軽で良いですね。